/* 
 * File:   Swap.cpp
 * Author: Gab'z
 * 
 * Created on 5 juin 2012, 18:16
 */

#include "Swap.h"
#include "../../Context.h"
#include "../literal/StackList.h"
#include "../literal/Integer.h"
#include "../../memento/StackMemento.h"

namespace calculator {

    Swap::Swap(const std::string name) : Operator(name) {
    }

    Swap::Swap(const Swap& orig) : Operator(orig._name) {
    }

    Swap::~Swap() {
    }

    Swap* Swap::clone() const {
        return new Swap(_name);
    }

    const Memento* Swap::createMemento() const throw (CommandException) {
        return new StackMemento(clone(), Context::getInstance()->getCurrentStack()->clone());
    }

    std::string Swap::isExecutable() const {
        unsigned int size = Context::getInstance()->getCurrentStack()->size();
        // soit 2 entiers dans la pile
        if (size != 2 && size < 4) return "Stack size must be 2 or >=4";
        if (size == 2) return "";
        // soit 2 entiers en tête et au moins 2 autres constantes
        // les 2 entiers doivent désigner des éléments de la pile (pas en dehors)
        const Integer* e1 = dynamic_cast<const Integer*> (Context::getInstance()->getCurrentStack()->getConstant(0));
        const Integer* e2 = dynamic_cast<const Integer*> (Context::getInstance()->getCurrentStack()->getConstant(1));
        if ((e1 == 0 || e2 == 0) || e1->value() < 1 || e2->value() < 1) return "The two first arguments must be positive Integer";
        unsigned int pos1 = (unsigned int) e1->value();
        unsigned int pos2 = (unsigned int) e2->value();
        if (pos1 == pos2) return "Swapping the same Constant...";
        unsigned int realSize = size - 2;
        if (pos1 > realSize || pos2 > realSize) return "Requesting Constant out of Stack";
        return "";
    }

    void Swap::eval(Stack* stack) const throw (CommandException) {
        unsigned int size = stack->size();
        // soit 2 entiers dans la pile
        if (size != 2 && size < 4) throw CommandException("Stack size must be 2 or >=4");
        if (size == 2) {
            // échange des 2 seuls Constant de la pile
            stack->swapTwoFirstConstant();
            return;
        }
        // soit 2 entiers en tête et au moins 2 autres constantes
        // les 2 entiers doivent désigner des éléments de la pile (pas en dehors)
        const Integer* e1 = dynamic_cast<const Integer*> (stack->getConstant(0));
        const Integer* e2 = dynamic_cast<const Integer*> (stack->getConstant(1));
        if ((e1 == 0 || e2 == 0) || e1->value() < 1 || e2->value() < 1) throw CommandException("The two first arguments must be positive Integer");
        unsigned int pos1 = (unsigned int) e1->value();
        unsigned int pos2 = (unsigned int) e2->value();
        if (pos1 == pos2) throw CommandException("Swapping the same Constant...");
        unsigned int realSize = size - 2;
        if (pos1 > realSize || pos2 > realSize) throw CommandException("Requesting Constant out of Stack");
        try {
            stack->swapAnyConstant(pos1, pos2);
        } catch (CommandException& e) {
            throw;
        }
        return;
    }
}